﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.SqlServer.Management.UI.VSIntegration;
using Skim.Ssms.AddIn.ActiveFileExplorer.Loader;
using SelectionContainer = Microsoft.VisualStudio.Shell.SelectionContainer;

namespace Skim.Ssms.AddIn.ActiveFileExplorer
{
    [Guid("6B1963AE-3328-477a-9C93-07F2BD03C06A")]
    public partial class ActiveFilesWindow : UserControl
    {
        // List of tool windows
        private readonly SelectionContainer _SelectionContainer = new SelectionContainer();
        private ITrackSelection _TrackSelection;

        /// <summary>
        /// Track selection service for the tool window.
        /// This should be set by the tool window pane as soon as the tool
        /// window is created.
        /// </summary>
        internal ITrackSelection TrackSelection
        {
            get { return _TrackSelection; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value", "TrackSelection");

                _TrackSelection = value;
                // Inititalize with an empty selection
                // Failure to do this would result in our later calls to 
                // OnSelectChange to be ignored (unless focus is lost
                // and regained).
                _SelectionContainer.SelectableObjects = null;
                _SelectionContainer.SelectedObjects = null;
                _TrackSelection.OnSelectChange(_SelectionContainer);
                //_SelectionContainer.SelectedObjectsChanged += selectionContainer_SelectedObjectsChanged;
            }
        }

        public ActiveFilesWindow()
        {
            InitializeComponent();
        }

        private void ActiveFilesWindow_Load(object sender, EventArgs e)
        {
            AssignImageList();
            LoadDisplayModes();
            LoadActiveFiles();
        }

        private void AssignImageList()
        {
            activeFileTreeView.ImageList = treeViewImageList;
        }

        private void LoadDisplayModes()
        {
            // ReSharper disable PossibleNullReferenceException
            displayModeComboBox.ComboBox.DataSource = GetDataSource();
            displayModeComboBox.ComboBox.DisplayMember = "DisplayText";
            // ReSharper restore PossibleNullReferenceException
            displayModeComboBox.ComboBox.ValueMember = "DocumentLoadType";
            if (displayModeComboBox.Items.Count > 0)
                displayModeComboBox.SelectedIndex = 0;
        }

        private static IList<DisplayModeComboBoxItem> GetDataSource()
        {
            return new List<DisplayModeComboBoxItem>
                       {
                           new DisplayModeComboBoxItem("Name", DocumentLoadType.Name),
                           new DisplayModeComboBoxItem("Document Type", DocumentLoadType.DocumentType),
                       }.AsReadOnly();
        }

        public void LoadActiveFiles()
        {
            ClearTreeView();
            LoadTreeView();
        }

        private void ClearTreeView()
        {
            activeFileTreeView.Nodes.Clear();
        }

        private void LoadTreeView()
        {
            DocumentLoaderFactory.Create(GetSelectedLoadType()).Load(activeFileTreeView, GetActiveFiles());
        }

        private DocumentLoadType GetSelectedLoadType()
        {
            return ((DisplayModeComboBoxItem)displayModeComboBox.SelectedItem).DocumentLoadType;
        }

        private static IEnumerable<Document> GetActiveFiles()
        {
            return from Document document in ServiceCache.ExtensibilityModel.Documents
                   orderby document.FullName
                   select document;
        }

        private void activeFileTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;

            var document = e.Node.Tag as Document;
            if (document == null) return;

            SelectActiveFile(document);
        }

        private static void SelectActiveFile(Document document)
        {
            document.Activate();
        }

        private void refreshButton_Click(object sender, EventArgs e)
        {
            LoadActiveFiles();
        }

        private void displayModeComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            LoadActiveFiles();
        }

        private void activeFileTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            PreventChangingSelectedImageIndex(e);
        }

        private void activeFileTreeView_KeyPress(object sender, KeyPressEventArgs e)
        {
            SelectedDocumentAction(document => document.Activate());
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SelectedDocumentAction(document => document.ActiveWindow.Close(vsSaveChanges.vsSaveChangesPrompt));
        }

        private void closeAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CloseDocuments(GetOpenDocuments());
        }

        private void closeAllButThisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var allButSelectedDocument = SelectedDocumentAction(
                selectedDocument =>
                    from document in GetOpenDocuments()
                    where document != selectedDocument
                    select document
            );
            CloseDocuments(allButSelectedDocument);
        }

        private static IEnumerable<Document> GetOpenDocuments()
        {
            return DocumentTypeFilter.FilterOpenDocuments(ServiceCache.ExtensibilityModel.Documents);
        }

        private static void CloseDocuments(IEnumerable<Document> documents)
        {
            foreach (var document in documents)
            {
                document.ActiveWindow.Close(vsSaveChanges.vsSaveChangesPrompt);
            }
        }

        private void SelectedDocumentAction(Action<Document> action)
        {
            if (GetSelectedDocument() == null)
                return;
            action(GetSelectedDocument());
        }

        private TResult SelectedDocumentAction<TResult>(Func<Document, TResult> method)
        {
            if (GetSelectedDocument() == null)
                return default(TResult);
            return method(GetSelectedDocument());
        }

        public Document GetSelectedDocument()
        {
            if (IsLeafNode(activeFileTreeView.SelectedNode))
                return activeFileTreeView.SelectedNode.Tag as Document;
            return null;
        }

        private static bool IsLeafNode(TreeNode node)
        {
            if (node == null)
                throw new ArgumentNullException("node", "Node is null");
            return (node.Nodes.Count <= 0) && ((node.Tag as Document) != null);
        }

        /// <remarks>
        /// When a user click on the item, don't change the icon
        /// </remarks>
        private static void PreventChangingSelectedImageIndex(TreeViewCancelEventArgs e)
        {
            e.Node.SelectedImageIndex = e.Node.ImageIndex;
        }

        private void documentActionMenu_Opening(object sender, CancelEventArgs e)
        {
            e.Cancel = !IsLeafNode(activeFileTreeView.SelectedNode);
        }

        private void copyFullPathToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(GetSelectedDocumentFullName());
        }

        private void openContainingFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string dir = Path.GetDirectoryName(GetSelectedDocumentFullName());
            if (!Directory.Exists(dir)) return;

            OpenInExplorer(dir);
        }

        private static void OpenInExplorer(string dir)
        {
            System.Diagnostics.Process.Start("explorer.exe", dir);
        }

        private string GetSelectedDocumentFullName()
        {
            return SelectedDocumentAction(document => document.FullName);
        }

        private void activeFileTreeView_MouseClick(object sender, MouseEventArgs e)
        {
            SelectNodeAt(e.Location);
        }

        /// <remarks>Mainly to move focus when a user "Right-Click" on a node</remarks>
        private void SelectNodeAt(Point selectedPoint)
        {
            if (activeFileTreeView.GetNodeAt(selectedPoint) == null) return;

            activeFileTreeView.SelectedNode = activeFileTreeView.GetNodeAt(selectedPoint);
        }
    }
}